package ca.etsmtl.log121.tp3.controller;

import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.JOptionPane;

import org.apache.commons.codec.digest.DigestUtils;

import ca.etsmtl.log121.tp3.command.Singleton;
import ca.etsmtl.log121.tp3.file.FileManager;
import ca.etsmtl.log121.tp3.model.ImageModel;
import ca.etsmtl.log121.tp3.model.MarkerModel;
import ca.etsmtl.log121.tp3.model.PerspectiveModel;
import ca.etsmtl.log121.tp3.swing.ApplicationFrame;
import ca.etsmtl.log121.tp3.view.PerspectiveView;
import ca.etsmtl.log121.tp3.view.ThumbnailView;

public class ApplicationController {
	private Map<String,ArrayList<MarkerModel>> markersMap;
	private ArrayList<PerspectiveModel> perspectiveList;
	private ImageModel image;
	private ThumbnailView view;
	private ApplicationFrame application;
	private String imageFileMD5;

	private State state = State.INITIALIZING;
	private Singleton singleton;

	public ApplicationController(ApplicationFrame application){
		this.application = application;

		markersMap = FileManager.loadMarkers();

		perspectiveList = new ArrayList<PerspectiveModel>();

		this.singleton = Singleton.getInstance();

		state = State.CLOSED;
	}

	public void openImage(InputStream stream) throws IOException {
		Image img = ImageIO.read(stream);

		imageFileMD5 = DigestUtils.md5Hex(stream);

		image = new ImageModel(img);
		view = new ThumbnailView(image);

		application.rebuildWorkspace(view);

		ArrayList<MarkerModel> markers = markersMap.get(imageFileMD5);

		// Need to be done after workspace rebuild
		if (markers != null) {
			if (!markers.isEmpty()) {
				for (MarkerModel marker : markers) {
					application.addMakerToMenu(marker);
				}
			}
		}

		newPerspective(); // Pre-open one perspective

		state = State.OPENED;

		application.refresh();
	}

	public void closeImage() {
		application.rebuildEmptyWorkspace();

		perspectiveList.clear();

		view = null;
		image = null;
		imageFileMD5 = null;

		state = State.CLOSED;

		application.refresh();
	}

	public boolean isImageOpened() {
		return (state == State.OPENED);
	}

	public void newPerspective() {
		PerspectiveModel perspective = new PerspectiveModel(image);
		PerspectiveView view = new PerspectiveView(perspective, this);

		perspective.addObserver(view);
		perspectiveList.add(perspective);
		singleton.addHandler();
		application.getTabbedPane().addTab(view);

		perspective.setName(application.getTabbedPane().getTitleAt(getCurrentTab()));
	}

	public void renamePerspective() {
		renamePerspective(getCurrentTab());
	}

	public void renamePerspective(int index) {
		String inputValue = (String) JOptionPane.showInputDialog(application.getMainPanel(), "Submit a new title for the current perspective", "Rename current perspective", JOptionPane.QUESTION_MESSAGE, null, null, ((PerspectiveView) application.getTabbedPane().getComponentAt(index)).getPerspective().getName());

		if (inputValue != null) {
			if (inputValue.isEmpty()) {
				JOptionPane.showMessageDialog(application.getMainPanel(), "The perspective title cannot be empty!", "Rename current perspective", JOptionPane.ERROR_MESSAGE, null);
			} else {
				renamePerspective(index, inputValue);
			}
		}
	}

	public void renamePerspective(int index, String name) {
		if (name == null) {
			throw new NullPointerException();
		}
		if (name.isEmpty()) {
			throw new IllegalArgumentException();
		}

		application.getTabbedPane().setTitleAt(index, name);

		((PerspectiveView) application.getTabbedPane().getComponentAt(index)).getPerspective().setName(name);
	}

	public void removePerspective() {
		removePerspective(getCurrentTab());
		singleton.removeHandler(getCurrentTab());
	}

	public void removePerspective(int index) {
		PerspectiveView view = (PerspectiveView) application.getTabbedPane().getComponentAt(index);
		PerspectiveModel perspective = view.getPerspective();

		application.getTabbedPane().removeTabAt(index);
		perspectiveList.remove(perspective);

		view = null;
		perspective = null;
	}

	public void newMarker() {
		renamePerspective(); // FIXME: Same, but with appropriate title and question

		MarkerModel marker = new MarkerModel(getCurrentPerspectiveView().getPerspective());

		application.addMakerToMenu(marker);

		ArrayList<MarkerModel> markers = markersMap.get(imageFileMD5);

		if (markers == null) {
			markers = new ArrayList<MarkerModel>();
		}

		markers.add(marker);

		markersMap.put(imageFileMD5, markers);

		FileManager.saveMakers(markersMap);
	}

	public int getCurrentTab() {
		return application.getTabbedPane().getSelectedIndex();
	}

	public PerspectiveView getCurrentPerspectiveView() {
		return (PerspectiveView) application.getTabbedPane().getComponentAt(getCurrentTab());
	}

	public void undo(){
		singleton.undo(getCurrentTab());
	}

	public void redo(){
		singleton.redo(getCurrentTab());
	}

	public void handleEvent(MouseEvent e, int[] mousePos, int[] posBase){
		singleton.setPerspective(perspectiveList.get(getCurrentTab()));
		singleton.dispatchCommand(e, getCurrentTab(), mousePos, posBase);
	}

	public void handleEvent(MouseWheelEvent e){
		singleton.setPerspective(perspectiveList.get(getCurrentTab()));
		singleton.dispatchCommand(e, getCurrentTab());
	}

	private enum State {
		INITIALIZING, CLOSED, OPENED;
	}
}
